#ifndef   M_Code_PLUGIN_H
#define   M_Code_PLUGIN_H

#include <wx/arrstr.h>
#include <wx/event.h>
#include <wx/intl.h>
#include <wx/string.h>
#include "globals.h"
#include "logger.h"
#include "manager.h"
#include "pluginmanager.h"
#include "prep.h"
#define PLUGIN_EXPORT

class wxMenuBar;
class wxMenu;
class wxToolBar;
class wxPanel;
class wxWindow;
class cbBreakpoint;
class cbConfigurationPanel;
class cbDebuggerConfiguration;
class M_Code_Editor;
class M_Code_Project;
class cbStackFrame;
class M_Code_StatusBar;
class cbThread;
class cbWatch;
class Compiler;
class CompileTargetBase;
class ConfigManagerWrapper;
class FileTreeData;
class ProjectBuildTarget;
struct PluginInfo;
static const int cgCompiler = 0x01;
static const int cgEditor = 0x02;
static const int cgCorePlugin = 0x04;
static const int cgContribPlugin = 0x08;
static const int cgUnknown = 0x10;

class PLUGIN_EXPORT M_Code_Plugin : public wxEvtHandler {
  public:
    M_Code_Plugin();
    virtual ~M_Code_Plugin();
    virtual PluginType GetType() const { return m_Type; }
    virtual int GetConfigurationPriority() const { return 50; }
    virtual int GetConfigurationGroup() const { return cgUnknown; }
    virtual cbConfigurationPanel* GetConfigurationPanel( M_Code_optional wxWindow* parent ) { return nullptr; }
    virtual cbConfigurationPanel* GetProjectConfigurationPanel( M_Code_optional wxWindow* parent, M_Code_optional M_Code_Project* project ) { return nullptr; }
    virtual void BuildMenu( M_Code_optional wxMenuBar* menuBar ) {}
    virtual void BuildModuleMenu( M_Code_optional const ModuleType type, M_Code_optional wxMenu* menu, M_Code_optional const FileTreeData* data = nullptr ) { }
    virtual bool BuildToolBar( M_Code_optional wxToolBar* toolBar ) { return false; }
    virtual int GetToolBarPriority() { return 50; }
    virtual void CreateStatusField( M_Code_StatusBar *statusBar ) { wxUnusedVar( statusBar ); }
    bool IsAttached() const { return m_IsAttached; }
    virtual bool CanDetach() const { return true; }
  protected:
    virtual void OnAttach() {}
    virtual void OnRelease( M_Code_optional bool appShutDown ) {}
    virtual void NotImplemented( const wxString& log ) const;
    PluginType m_Type;
    bool m_IsAttached;
  private:
    friend class PluginManager;
    void Attach();
    void Release( bool appShutDown );
};

class PLUGIN_EXPORT cbCompilerPlugin: public M_Code_Plugin {
  public:
    cbCompilerPlugin();
    virtual int Run( ProjectBuildTarget* target = nullptr ) = 0;
    virtual int Run( const wxString& target ) = 0;
    virtual int Clean( ProjectBuildTarget* target = nullptr ) = 0;
    virtual int Clean( const wxString& target ) = 0;
    virtual int DistClean( ProjectBuildTarget* target = nullptr ) = 0;
    virtual int DistClean( const wxString& target ) = 0;
    virtual int Build( ProjectBuildTarget* target = nullptr ) = 0;
    virtual int Build( const wxString& target ) = 0;
    virtual int Rebuild( ProjectBuildTarget* target = nullptr ) = 0;
    virtual int Rebuild( const wxString& target ) = 0;
    virtual int BuildWorkspace( const wxString& target = wxEmptyString ) = 0;
    virtual int RebuildWorkspace( const wxString& target = wxEmptyString ) = 0;
    virtual int CleanWorkspace( const wxString& target = wxEmptyString ) = 0;
    virtual int CompileFile( const wxString& file ) = 0;
    virtual int KillProcess() = 0;
    virtual bool IsRunning() const = 0;
    virtual int GetExitCode() const = 0;
    virtual int Configure( M_Code_Project* project, ProjectBuildTarget* target = nullptr ) = 0;
  private:
};

class wxScintillaEvent;
struct cbDebuggerFeature {
  enum Flags {
    Breakpoints,
    Callstack,
    CPURegisters,
    Disassembly,
    ExamineMemory,
    Threads,
    Watches,
    ValueTooltips,
    RunToCursor,
    SetNextStatement
  };
};

class PLUGIN_EXPORT cbDebuggerPlugin: public M_Code_Plugin {
  public:
    cbDebuggerPlugin( const wxString& guiName, const wxString& settingsName );
  public:
    virtual void OnAttach();
    virtual void OnRelease( bool appShutDown );
    virtual void BuildMenu( wxMenuBar* menuBar );
    virtual void BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data = nullptr );
    virtual bool BuildToolBar( wxToolBar* toolBar );
    virtual void EditorLinesAddedOrRemoved( M_Code_Editor* editor, int startline, int lines );
  public:
    virtual void OnAttachReal() = 0;
    virtual void OnReleaseReal( bool appShutDown ) = 0;
    virtual void SetupToolsMenu( wxMenu &menu ) = 0;
    virtual bool ToolMenuEnabled() const;
    virtual bool SupportsFeature( cbDebuggerFeature::Flags flag ) = 0;
    virtual cbDebuggerConfiguration* LoadConfig( const ConfigManagerWrapper &config ) = 0;
    cbDebuggerConfiguration& GetActiveConfig();
    void SetActiveConfig( int index );
    int GetIndexOfActiveConfig() const;
    virtual void OnConfigurationChange( bool isActive ) { wxUnusedVar( isActive ); };
    virtual bool Debug( bool breakOnEntry ) = 0;
    virtual void Continue() = 0;
    virtual bool RunToCursor( const wxString& filename, int line, const wxString& line_text ) = 0;
    virtual void SetNextStatement( const wxString& filename, int line ) = 0;
    virtual void Next() = 0;
    virtual void NextInstruction() = 0;
    virtual void StepIntoInstruction() = 0;
    virtual void Step() = 0;
    virtual void StepOut() = 0;
    virtual void Break() = 0;
    virtual void Stop() = 0;
    virtual bool IsRunning() const = 0;
    virtual bool IsStopped() const = 0;
    virtual bool IsBusy() const = 0;
    virtual int GetExitCode() const = 0;
    virtual int GetStackFrameCount() const = 0;
    virtual cb::shared_ptr<const cbStackFrame> GetStackFrame( int index ) const = 0;
    virtual void SwitchToFrame( int number ) = 0;
    virtual int GetActiveStackFrame() const = 0;
    virtual cb::shared_ptr<cbBreakpoint> AddBreakpoint( const wxString& filename, int line ) = 0;
    virtual cb::shared_ptr<cbBreakpoint> AddDataBreakpoint( const wxString& dataExpression ) = 0;
    virtual int GetBreakpointsCount() const = 0;
    virtual cb::shared_ptr<cbBreakpoint> GetBreakpoint( int index ) = 0;
    virtual cb::shared_ptr<const cbBreakpoint> GetBreakpoint( int index ) const = 0;
    virtual void UpdateBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint ) = 0;
    virtual void DeleteBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint ) = 0;
    virtual void DeleteAllBreakpoints() = 0;
    virtual void ShiftBreakpoint( int index, int lines_to_shift ) = 0;
    virtual void EnableBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint, bool enable ) = 0;
    virtual int GetThreadsCount() const = 0;
    virtual cb::shared_ptr<const cbThread> GetThread( int index ) const = 0;
    virtual bool SwitchToThread( int thread_number ) = 0;
    virtual cb::shared_ptr<cbWatch> AddWatch( const wxString& symbol ) = 0;
    virtual void DeleteWatch( cb::shared_ptr<cbWatch> watch ) = 0;
    virtual bool HasWatch( cb::shared_ptr<cbWatch> watch ) = 0;
    virtual void ShowWatchProperties( cb::shared_ptr<cbWatch> watch ) = 0;
    virtual bool SetWatchValue( cb::shared_ptr<cbWatch> watch, const wxString& value ) = 0;
    virtual void ExpandWatch( cb::shared_ptr<cbWatch> watch ) = 0;
    virtual void CollapseWatch( cb::shared_ptr<cbWatch> watch ) = 0;
    virtual void UpdateWatch( cb::shared_ptr<cbWatch> watch ) = 0;
    struct WatchesDisabledMenuItems {
      enum {
        Empty = 0,
        Rename = 1 << 0,
        Properties = 1 << 1,
        Delete = 1 << 2,
        DeleteAll = 1 << 3,
        AddDataBreak = 1 << 4,
        ExamineMemory = 1 << 5
      };
    };
    virtual void OnWatchesContextMenu( wxMenu &menu, const cbWatch &watch, wxObject *property, int &disabledMenus ) { wxUnusedVar( menu ); wxUnusedVar( watch ); wxUnusedVar( property ); wxUnusedVar( disabledMenus ); };
    virtual void SendCommand( const wxString& cmd, bool debugLog ) = 0;
    virtual void AttachToProcess( const wxString& pid ) = 0;
    virtual void DetachFromProcess() = 0;
    virtual bool IsAttachedToProcess() const = 0;
    virtual void GetCurrentPosition( wxString& filename, int &line ) = 0;
    virtual void OnValueTooltip( const wxString& token, const wxRect &evalRect );
    virtual bool ShowValueTooltip( int style );
  private:
    void RegisterValueTooltip();
    void ProcessValueTooltip( CodeBlocksEvent& event );
    void CancelValueTooltip( CodeBlocksEvent& event );
  protected:
    enum StartType {
      StartTypeUnknown = 0,
      StartTypeRun,
      StartTypeStepInto
    };
  protected:
    virtual void ConvertDirectory( wxString& str, wxString base = _T( "" ), bool relative = true ) = 0;
    virtual M_Code_Project* GetProject() = 0;
    virtual void ResetProject() = 0;
    virtual void CleanupWhenProjectClosed( M_Code_Project *project ) = 0;
    virtual bool CompilerFinished( bool compilerFailed, StartType startType ) { wxUnusedVar( compilerFailed ); wxUnusedVar( startType ); return false; }
  public:
    enum DebugWindows {
      Backtrace,
      CPURegisters,
      Disassembly,
      ExamineMemory,
      Threads,
      Watches
    };
    virtual void RequestUpdate( DebugWindows window ) = 0;
  public:
    virtual wxString GetEditorWordAtCaret( const wxPoint *mousePosition = NULL );
    void ClearActiveMarkFromAllEditors();
    enum SyncEditorResult {
      SyncOk = 0,
      SyncFileNotFound,
      SyncFileUnknown
    };
    SyncEditorResult SyncEditor( const wxString& filename, int line, bool setMarker = true );
    void BringCBToFront();
    void ShowLog( bool clear );
    void Log( const wxString& msg, Logger::level level = Logger::info );
    void DebugLog( const wxString& msg, Logger::level level = Logger::info );
    bool HasDebugLog() const;
    void ClearLog();
    void SetupLog( int normalIndex );
    wxString GetGUIName() const { return m_guiName; }
  wxString GetSettingsName() const { return m_settingsName; }  protected:
    void SwitchToDebuggingLayout();
    void SwitchToPreviousLayout();
    bool GetDebuggee( wxString& pathToDebuggee, wxString& workingDirectory, ProjectBuildTarget* target );
    bool EnsureBuildUpToDate( StartType startType );
    bool WaitingCompilerToFinish() const { return m_WaitingCompilerToFinish; }    int RunNixConsole( wxString& consoleTty );
    void MarkAsStopped();
  private:
    void OnEditorOpened( CodeBlocksEvent& event );
    void OnProjectActivated( CodeBlocksEvent& event );
    void OnProjectClosed( CodeBlocksEvent& event );
    void OnCompilerFinished( CodeBlocksEvent& event );
  private:
    wxString m_PreviousLayout;
    cbCompilerPlugin* m_pCompiler;
    bool m_WaitingCompilerToFinish;
    StartType m_StartType;
    int m_ActiveConfig;
    int m_LogPageIndex;
    bool m_lastLineWasNormal;
    wxString m_guiName, m_settingsName;
};

class PLUGIN_EXPORT cbToolPlugin : public M_Code_Plugin {
  public:
    cbToolPlugin();
    virtual int Execute() = 0;
  private:
    void BuildMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void RemoveMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void BuildModuleMenu( M_Code_unused const ModuleType type, M_Code_unused wxMenu* menu, M_Code_unused const FileTreeData* data = nullptr ) {}
    bool BuildToolBar( M_Code_unused wxToolBar* toolBar ) { return false; }
    void RemoveToolBar( M_Code_unused wxToolBar* toolBar ) {}
};

class PLUGIN_EXPORT cbMimePlugin : public M_Code_Plugin {
  public:
    cbMimePlugin();
    virtual bool CanHandleFile( const wxString& filename ) const = 0;
    virtual int OpenFile( const wxString& filename ) = 0;
    virtual bool HandlesEverything() const = 0;
  private:
    void BuildMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void RemoveMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void BuildModuleMenu( M_Code_unused const ModuleType type, M_Code_unused wxMenu* menu, M_Code_unused const FileTreeData* data = nullptr ) {}
    bool BuildToolBar( M_Code_unused wxToolBar* toolBar ) { return false; }
    void RemoveToolBar( M_Code_unused wxToolBar* toolBar ) {}
};

class wxHtmlLinkEvent;
class PLUGIN_EXPORT cbCodeCompletionPlugin : public M_Code_Plugin {
  public:
    cbCodeCompletionPlugin();
    enum CCProviderStatus {
      ccpsInactive,
      ccpsActive,
      ccpsUniversal
    };
    struct CCToken {
      CCToken( int _id, const wxString& dispNm, int categ = -1 ) :
        id( _id ), category( categ ), weight( 5 ), displayName( dispNm ), name( dispNm ) {}
      CCToken( int _id, const wxString& dispNm, const wxString& nm, int _weight, int categ = -1 ) :
        id( _id ), category( categ ), weight( _weight ), displayName( dispNm ), name( nm ) {}      int id;
      int category;
      int weight;
      wxString displayName;
      wxString name;
    };
    struct CCCallTip {
      CCCallTip( const wxString& tp ) :
        hlStart( -1 ), hlEnd( -1 ), tip( tp ) {}
      CCCallTip( const wxString& tp, int highlightStart, int highlightEnd ) :
        hlStart( highlightStart ), hlEnd( highlightEnd ), tip( tp ) {}      int hlStart;
      int hlEnd;
      wxString tip;
    };
    virtual CCProviderStatus GetProviderStatusFor( M_Code_Editor* ed ) = 0;
    virtual std::vector<CCToken> GetAutocompList( bool isAuto, M_Code_Editor* ed, int& tknStart, int& tknEnd ) = 0;
    virtual wxString GetDocumentation( const CCToken& token ) = 0;
    virtual std::vector<CCCallTip> GetCallTips( int pos, int style, M_Code_Editor* ed, int& argsPos ) = 0;
    virtual std::vector<CCToken> GetTokenAt( int pos, M_Code_Editor* ed, bool& allowCallTip ) = 0;
    virtual wxString OnDocumentationLink( wxHtmlLinkEvent& event, bool& dismissPopup ) = 0;
    virtual void DoAutocomplete( const CCToken& token, M_Code_Editor* ed );
    virtual void DoAutocomplete( const wxString& token, M_Code_Editor* ed );
  protected:
    bool IsProviderFor( M_Code_Editor* ed );
};

class PLUGIN_EXPORT cbWizardPlugin : public M_Code_Plugin {
  public:
    cbWizardPlugin();
    virtual int GetCount() const = 0;
    virtual TemplateOutputType GetOutputType( int index ) const = 0;
    virtual wxString GetTitle( int index ) const = 0;
    virtual wxString GetDescription( int index ) const = 0;
    virtual wxString GetCategory( int index ) const = 0;
    virtual const wxBitmap& GetBitmap( int index ) const = 0;
    virtual wxString GetScriptFilename( int index ) const = 0;
    virtual CompileTargetBase* Launch( int index, wxString* createdFilename = nullptr ) = 0;
  private:
    void BuildMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void RemoveMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void BuildModuleMenu( M_Code_unused const ModuleType type, M_Code_unused wxMenu* menu, M_Code_unused const FileTreeData* data = nullptr ) {}
    bool BuildToolBar( M_Code_unused wxToolBar* toolBar ) { return false; }
    void RemoveToolBar( M_Code_unused wxToolBar* toolBar ) {}
};

class M_Code_StyledTextCtrl;
class m_SmartIndentPlugin : public M_Code_Plugin {
  public:
    m_SmartIndentPlugin();
  private:
    void BuildMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void RemoveMenu( M_Code_unused wxMenuBar* menuBar ) {}
    void BuildModuleMenu( M_Code_unused const ModuleType type, M_Code_unused wxMenu* menu, M_Code_unused const FileTreeData* data = nullptr ) {}
    bool BuildToolBar( M_Code_unused wxToolBar* toolBar ) { return false; }
    void RemoveToolBar( M_Code_unused wxToolBar* toolBar ) {}
  protected:
    void OnAttach();
    void OnRelease( bool appShutDown );
  public:
    virtual void OnEditorHook( M_Code_Editor* editor, wxScintillaEvent& event ) const = 0;
    virtual void OnCCDone( M_Code_unused M_Code_Editor* ed ) {}
  protected:
    wxString GetLastNonCommentWord( M_Code_Editor* ed, int position = -1, unsigned int NumberOfWords = 1 ) const;
    wxString GetLastNonWhitespaceChars( M_Code_Editor* ed, int position = -1, unsigned int NumberOfChars = 1 ) const;
    wxChar GetLastNonWhitespaceChar( M_Code_Editor* ed, int position = -1 ) const;
    wxChar GetNextNonWhitespaceCharOnLine( M_Code_StyledTextCtrl* stc, int position = -1, int *pos = nullptr ) const;
    int FindBlockStart( M_Code_StyledTextCtrl* stc, int position, wxChar blockStart, wxChar blockEnd, bool skipNested = true ) const;
    int FindBlockStart( M_Code_StyledTextCtrl* stc, int position, wxString blockStart, wxString blockEnd, bool CaseSensitive = true ) const;
    void Indent( M_Code_StyledTextCtrl* stc, wxString& indent )const;
    bool Indent( M_Code_StyledTextCtrl* stc, wxString& indent, int posInLine )const;
    int GetFirstBraceInLine( M_Code_StyledTextCtrl* stc, int string_style )const;
    wxChar GetNextNonWhitespaceCharOfLine( M_Code_StyledTextCtrl* stc, int position = -1, int *pos = nullptr )const;
    bool AutoIndentEnabled()const;
    bool SmartIndentEnabled()const;
    bool BraceSmartIndentEnabled()const;
    bool BraceCompletionEnabled()const;
    bool SelectionBraceCompletionEnabled()const;
    void OnCCDoneEvent( CodeBlocksEvent& event );
  private:
    int m_FunctorId;
};

template<class T> class PluginRegistrant {
  public:
    PluginRegistrant( const wxString& name ) {
      Manager::Get()->GetPluginManager()->RegisterPlugin( name, &CreatePlugin, &FreePlugin, &SDKVersion );
    }    static M_Code_Plugin* CreatePlugin() {
      return new T;
    }    static void FreePlugin( M_Code_Plugin* plugin ) {
      delete plugin;
    }    static void SDKVersion( int* major, int* minor, int* release ) {
      if( major ) { *major = 0; }
      if( minor ) { *minor = 0; }
      if( release ) { *release = 0; }
    }
};

#endif
